package appmanager

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"

	"gitee.com/huoyu123hk/cloudAgent/pkg/logx"
)

// FileResponse represents the response structure for file operations
type FileResponse struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// VersionInfo represents version information for an app
type VersionInfo struct {
	Version  string `json:"version"`
	FileHash string `json:"fileHash"`
	Name     string `json:"name"`
}

// FileManager handles file operations
type FileManager struct {
	api     string
	headers map[string]string
}

// NewFileManager creates a new FileManager
func NewFileManager(token string, api string) *FileManager {
	headers := map[string]string{
		"token": token,
	}

	return &FileManager{
		api:     api,
		headers: headers,
	}
}

func (fm *FileManager) httpRequest(method, url string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	for k, v := range fm.headers {
		req.Header.Set(k, v)
	}

	client := &http.Client{}
	return client.Do(req)
}

// download downloads a file by its MD5 hash
func (fm *FileManager) Download(key string) string {
	fileURL := fmt.Sprintf("%s/api/pipeline/artifact/download", fm.api)

	// 创建HTTP请求
	req, err := http.NewRequest("GET", fileURL, nil)
	if err != nil {
		logx.Errorf("Failed to create request: %v", err)
		return ""
	}

	// 设置查询参数
	q := req.URL.Query()
	q.Add("md5", key)
	req.URL.RawQuery = q.Encode()

	// 设置请求头
	for k, v := range fm.headers {
		req.Header.Set(k, v)
	}

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logx.Errorf("Failed to download file: %v", err)
		return ""
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logx.Errorf("Download failed with status: %d", resp.StatusCode)
		return ""
	}

	// 获取文件名
	contentDisposition := resp.Header.Get("Content-Disposition")
	_, params, err := mime.ParseMediaType(contentDisposition)
	if err != nil {
		logx.Errorf("Failed to parse content disposition: %v", err)
		return ""
	}

	filename := params["filename"]
	if filename == "" {
		filename = key
	}

	// 创建文件
	file, err := os.Create(filename)
	if err != nil {
		logx.Errorf("Failed to create file: %v", err)
		return ""
	}
	defer file.Close()

	// 写入文件内容
	_, err = io.Copy(file, resp.Body)
	if err != nil {
		logx.Errorf("Failed to write file: %v", err)
		return ""
	}

	return filename
}

// getLatestVersion gets the latest version of apps for a project
func (fm *FileManager) GetLatestVersion(project string) []VersionInfo {
	url := fmt.Sprintf("%s/api/pipeline/artifact/project/%s/latest/list", fm.api, project)

	resp, err := fm.httpRequest("GET", url, nil)
	if err != nil {
		logx.Errorf("Failed to get latest version: %v", err)
		return nil
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		logx.Errorf("Get latest version failed with status: %d", resp.StatusCode)
		return nil
	}

	// 解析响应
	var response FileResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		logx.Errorf("Failed to decode response: %v", err)
		return nil
	}

	if response.Code != 200 {
		logx.Errorf("Get latest version failed, error message: %s", response.Msg)
		return nil
	}

	// 类型断言获取数据
	data, ok := response.Data.([]interface{})
	if !ok {
		logx.Error("Invalid data format")
		return nil
	}

	// 转换为VersionInfo数组
	var versions []VersionInfo
	for _, item := range data {
		itemMap, ok := item.(map[string]interface{})
		if !ok {
			continue
		}

		version := VersionInfo{
			Version:  fmt.Sprintf("%s", itemMap["version"]),
			FileHash: fmt.Sprintf("%s", itemMap["fileHash"]),
			Name:     fmt.Sprintf("%s", itemMap["name"]),
		}
		versions = append(versions, version)
	}

	return versions
}

func (fm *FileManager) GetAppInfo(project, name string) *VersionInfo {
	versions := fm.GetLatestVersion(project)
	for _, v := range versions {
		if v.Name == name {
			return &v
		}
	}
	return nil
}

// calculateMD5 计算文件的MD5值
func (fm *FileManager) CalculateMD5(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hash := md5.New()
	if _, err := io.Copy(hash, file); err != nil {
		return "", err
	}

	return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

// verifyMD5 验证文件的MD5值
func (fm *FileManager) VerifyMD5(filePath, expectedMD5 string) bool {
	// 检查文件是否存在
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		logx.Infof("文件不存在: %s\n", filePath)
		return false
	}

	// 计算文件的MD5值
	actualMD5, err := fm.CalculateMD5(filePath)
	if err != nil {
		logx.Errorf("计算MD5时出错: %v", err)
		return false
	}

	// 比较MD5值
	if actualMD5 != expectedMD5 {
		logx.Errorf("MD5不匹配: 期望 %s, 实际 %s", expectedMD5, actualMD5)
		return false
	}
	return true
}

// uploadFile uploads a file and returns the result
func (fm *FileManager) UploadFile(filename string) string {
	uploadURL := fmt.Sprintf("%s/api/sys/files/upload", fm.api)

	// 打开文件
	file, err := os.Open(filename)
	if err != nil {
		logx.Errorf("Failed to open file: %v", err)
		return ""
	}
	defer file.Close()

	// 创建multipart表单数据
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 添加文件字段
	part, err := writer.CreateFormFile("file", filepath.Base(filename))
	if err != nil {
		logx.Errorf("Failed to create form file: %v", err)
		return ""
	}

	// 复制文件内容到表单
	_, err = io.Copy(part, file)
	if err != nil {
		logx.Errorf("Failed to copy file to form: %v", err)
		return ""
	}

	// 关闭multipart writer
	err = writer.Close()
	if err != nil {
		logx.Errorf("Failed to close writer: %v", err)
		return ""
	}

	// 创建HTTP请求
	req, err := http.NewRequest("POST", uploadURL, body)
	if err != nil {
		logx.Errorf("Failed to create request: %v", err)
		return ""
	}

	// 设置请求头
	req.Header.Set("Content-Type", writer.FormDataContentType())
	for k, v := range fm.headers {
		req.Header.Set(k, v)
	}

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		logx.Errorf("Failed to upload file: %v", err)
		return ""
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		// 读取错误响应体
		respBody, _ := io.ReadAll(resp.Body)
		logx.Errorf("Upload failed with status: %d, response: %s", resp.StatusCode, string(respBody))
		return ""
	}

	// 解析响应
	var response FileResponse
	if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
		logx.Errorf("Failed to decode response: %v", err)
		return ""
	}

	if response.Code != 200 {
		logx.Errorf("Upload file failed, error message: %s", response.Msg)
		return ""
	}

	// 返回上传结果
	if data, ok := response.Data.(string); ok {
		return data
	}

	return ""
}
